package mainPackage;
import Memmory.*;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class VirtualMachine {
    public VirtualMemmory virtualMemmory;
    private RealMachine realMachine;
    
    public static int DATA_SEG_SIZE = 128;
    public static int CODE_SEG_SIZE = 128;
    
    public VirtualMachine(RealMachine rm) {
        realMachine = rm;
        
        virtualMemmory = new VirtualMemmory(rm, DATA_SEG_SIZE, CODE_SEG_SIZE);
    }
    
    public void performStep() {
        Word word = virtualMemmory.getWord(realMachine.IC[0], realMachine.IC[1]);
        String cmd = Interpreter.fromWord(word);
        byte[] value = new byte[4];
        value = word.getBytes();

        switch (cmd) {
            case "TOASCII":
                realMachine.TOASCII();
                break;
            case "ADD":
                realMachine.ADD();
                break;
            case "SUB":
                realMachine.SUB();
                break;
            case "MUL":
                realMachine.MUL();
                break;
            case "DIV":
                realMachine.DIV();
                break;
            case "CMP":
                realMachine.CMP();
                break;
            case "MOVAB":
                realMachine.MOVAB();
                break;
            case "MOVBA":
                realMachine.MOVBA();
                break;
            case "LOAD":
                this.LOAD(value[2], value[3]);
                break;
            case "SET":
                this.SET(value[2], value[3]);
                break;
            case "JMP":
                this.JMP(value[2], value[3]);
                break;
            case "JMPE":
                this.JMPE(value[2], value[3]);
                break;
            case "GD":
                this.GD(value[2], value[3]);
                break;
            case "PD":
                this.PD(value[2], value[3]);
                break;
            case "HALT":
                this.HALT();
                break;
            default:
                this.NONE();
        }
        
        realMachine.setIC(realMachine.getIC() + 1);
    }
    
    private void NONE() {
        realMachine.PI = 2;
    }
    
    private void HALT() {
        realMachine.SI = 3;
    }
    
    private void LOAD(byte x, byte y) {
        //System.out.println("load A = " + virtualMemmory.getWord(x).getNumber());
        realMachine.A.setValue(virtualMemmory.getWord(x, y).getBytes());
    }
    
    private void SET(byte x, byte y) {
        virtualMemmory.putWord(new Word(realMachine.A.getNumber()), x, y);
    }
    
    private void JMP(byte x, byte y) {
        if (x == 0) {
            realMachine.setIC(realMachine.getIC() - y);
        } else {
            realMachine.setIC(realMachine.getIC() + y);
        }
    }
    
    private void JMPE(byte x, byte y) {
        if (realMachine.L == 0) {
            if (x == 0) {
                realMachine.setIC(realMachine.getIC() - y);
            } else {
                realMachine.setIC(realMachine.getIC() + y);
            }
        }
    }
    
    private void GD(byte x, byte y) {
        realMachine.SI = 1;
        
        Word destAddrWord = new Word();
        int realAddr = virtualMemmory.translateToRealAddress(x * RealMachine.BLOCK_SIZE + y);
        destAddrWord.setValue(0, 0, realAddr / RealMachine.BLOCK_SIZE, realAddr % RealMachine.BLOCK_SIZE);
        
        realMachine.MEMMORY.putWord(new Word(0), 98, 0); //kryptis
        realMachine.MEMMORY.putWord(new Word(0), 98, 1); //source
        realMachine.MEMMORY.putWord(destAddrWord, 98, 2); //destination
    }
    
    private void PD(byte x, byte y) {
        realMachine.SI = 2;
        
        Word sourceAddrWord = new Word();
        int realAddr = virtualMemmory.translateToRealAddress(x * RealMachine.BLOCK_SIZE + y);
        sourceAddrWord.setValue(0, 0, realAddr / RealMachine.BLOCK_SIZE, realAddr % RealMachine.BLOCK_SIZE);
        
        realMachine.MEMMORY.putWord(new Word(0), 98, 0); //kryptis
        realMachine.MEMMORY.putWord(sourceAddrWord, 98, 1); //source
        realMachine.MEMMORY.putWord(new Word(0), 98, 2); //destination
    }
    
    public void loadProgram(String URL) throws FileNotFoundException, IOException {
        BufferedReader reader = new BufferedReader(new FileReader(URL));
        
        String line;
        int memPointer = 0;
        boolean toDataSeg = true;
        
        while ((line = reader.readLine()) != null) {
            line = line.trim();
            line = line.replaceAll("\t", "");
            
            
            if (line.equals("DATASEG:")) {
                continue;
            }
            if (line.equals("CODESEG:")) {
                toDataSeg = false;
                memPointer = virtualMemmory.getCodeSegSize();
                continue;
            }
            
            if (line.startsWith("//")) {
                continue;
            }
            
            if (line.equals("")) {
                continue;
            }
            
            if (toDataSeg) {
                String[] parts = line.split("<<");
                Word word;
                
                if (parts[1].startsWith("'") && parts[1].endsWith("'")) { //tikrinam ar tarp kabuciu ' '
                    String noQuotes = parts[1].replace("'", "");
                    word = new Word(noQuotes.getBytes());
                } else {
                    String[] wordParts = parts[1].split(",");
                    if (wordParts.length == 4) {
                        word = new Word(Integer.parseInt(wordParts[0]), Integer.parseInt(wordParts[1]), Integer.parseInt(wordParts[2]), Integer.parseInt(wordParts[3]));
                    } else {
                        word = new Word(Integer.parseInt(parts[1])); //kitu atveju skaicius
                    }
                }
                
                String[] addressParts = parts[0].split(",");
                virtualMemmory.putWord(word, Integer.parseInt(addressParts[0]), Integer.parseInt(addressParts[1]));
                
            } else {
                Word word = Interpreter.fromString(line);
                virtualMemmory.putWord(word, memPointer);
                memPointer++;
            }
        }
    }
    
    public void viewMemory() {
        Word word;
        byte[] value;
        
        for (int i = 0; i < virtualMemmory.getDataSegSize() + virtualMemmory.getCodeSegSize(); i++) {
            word = virtualMemmory.getWord(i);
            value = word.getBytes();
            for (int j = 0; j < 4; j++) {
                System.out.print((value[j] & 0xFF) + " ");
            }
            System.out.println();
        }
    }
}
